package com.example.demo.word;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;

/**
 * 检索工具类
 *
 * @author jinyh
 * @date 2021/7/4
 */
@Slf4j
public class SearchBaseOnSimHash {

    // 常量定义：数据库中相似记录的 id
    public static final String idKey = "id";

    // 常量定义：数据库中相似记录的 simHash 指纹
    public static final String fingerKey = "finger";

    // 相似的汉明距离阀值
    private static int similarityThreshold = 3;

    /**
     * 根据 SimHash 值检索文本相似的记录编号，算法流程：
     * 1、将 SimHash 分成四段，以每一段为 Key 在 全局表中查找当前段的完整指纹
     * 2、如果当前 SimHash 匹配到了缓冲中某一段的信息，则计算改匹配段的指纹和当前指纹的距离
     * 3、如果距离小于相似度阀门值，则视为找到，返回对象；
     * 4、否则，视为无相似记录
     *
     * @param segments 分段
     * @param simHash
     * @return
     */
    public static JSONObject search(String[] segments, String simHash) {
        if (segments == null || segments.length != 4) {
            return null;
        }

        // 创建一个 SimHash 对象，用于计算汉明距离
        SimHashBaseOnWord simHashByWord = new SimHashBaseOnWord();

        // 逐段遍历，查找计算文本相似的记录
        long start = System.currentTimeMillis();
        for (String segment : segments) {
            // Key 视为 某一个分段 Hash，值是一个对象
            //Map<String, String> hashSets = RedisUtil.get(segment);
            Map<String, String> hashSets = new HashMap<>();

            if (hashSets == null) {
                continue;
            }

            // 匹配到某一段，则计算汉明距离
            String finger = hashSets.get(fingerKey);
            int hammingDistance = simHashByWord.hammingDistance(finger, simHash);
            if (hammingDistance <= similarityThreshold) {
                long end = System.currentTimeMillis();
                JSONObject responseObj = new JSONObject();
                responseObj.put(idKey, hashSets.get(idKey));
                return responseObj;
            }
        }

        return null;
    }

    /**
     * 向索引中添加一个 SimHash 的记录，分段遍历，将各个段加入缓存
     *
     * @param segments 分段，因为分段信息在 search 和 push 是都会用到，为了避免重复操作，作为参数
     * @param simHash
     * @param data
     */
    public static void push(String[] segments, String simHash, JSONObject data) {
        if (segments == null || segments.length != 4) {
            return;
        }

        Map<String, String> redisData = new HashMap<>();
        redisData.put(fingerKey, simHash);
        redisData.put(idKey, data.getString(idKey));

        for (String segment : segments) {
            //RedisUtil.put(segment, redisData);
        }
    }

    public static void main(String[] args) {
        String text1 = "我的兴趣爱好是看书";
        String text2 = "看书是我的兴趣爱好";
        String text3 = "我爱好看书";

        SimHashBaseOnWord textSimilarity = new SimHashBaseOnWord();
        String simhash = textSimilarity.simHash("我的兴趣爱好是看书");

        String[] hashs = new String[4];
        hashs[0] = simhash.substring(0, 16);
        hashs[1] = simhash.substring(16, 32);
        hashs[2] = simhash.substring(32, 48);
        hashs[3] = simhash.substring(48, 64);
        JSONObject target = search(hashs, simhash);

        // 没有找到相似记录，则插入到缓存
        if (target == null) {
            target = new JSONObject();
            target.put(idKey, "123");
            push(hashs, simhash, target);
        } else {
            System.out.println(target.get(idKey));
        }

        String sim2 = textSimilarity.simHash("看书是我的兴趣爱好");
        hashs[0] = sim2.substring(0, 16);
        hashs[1] = sim2.substring(16, 32);
        hashs[2] = sim2.substring(32, 48);
        hashs[3] = sim2.substring(48, 64);
        target = search(hashs, sim2);

        // 没有找到相似记录，则插入到缓存
        if (target == null) {
            target = new JSONObject();
            target.put(idKey, "456");
            push(hashs, sim2, target);
        } else {
            System.out.println(target.get(idKey));
        }
    }
}
